
package de.unihannover.se.restlab.microblog;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.core.util.MultivaluedMapImpl;

import de.unihannover.se.restlab.microblog.models.Account;
import de.unihannover.se.restlab.microblog.models.Link;
import de.unihannover.se.restlab.microblog.models.Message;
import de.unihannover.se.restlab.microblog.models.User;

public class MicroClient {

	private static Client c;

	private static User[] users;
	private static Message[] msgs;
	private static Account[] accounts;

	private static Link messagesLink;
	private static Link usersLink;
	private static Link accountsLink;

	private static String storedUsername, storedPassword;

	public static void main (String[] args) {
		c = Client.create();

		updateLinks();
		updateUsers();
		updateMessages();

		boolean done = false;
		String sepStr = sep('=', 40) + "\n";

		while (!done) {
			l(sepStr + "Optionen\n" + sepStr + "1. Nutzerliste anzeigen\n" + "2. Nachrichtenliste anzeigen\n"
				+ "3. Nutzer anlegen\n" + "4. Nachricht schreiben\n" + "5. Accountliste anzeigen\n" + "6. Beenden");

			try {
				int sel = Integer.valueOf(prompt("Auswahl"));
				switch (sel) {
				case 1:
					showUserList();
					break;
				case 2:
					String filterQuestion = prompt("Nach bestimmtem Nutzer filtern? (y/n)");
					showMessageList(filterQuestion.startsWith("y") ? getId(prompt("Name des Nutzers")) : -1);
					break;
				case 3:
					addUser();
					break;
				case 4:
					addMessage();
					break;
				case 5:
					showAccountList();
					break;
				case 6:
					done = true;
					break;
				default:
				}
				pause();
			} catch (Exception e) {
				System.out.println("Fehler: bitte neue Eingaben tätigen.");
				e.printStackTrace();
			}
		}
	}

	private static void pause () throws Exception {
		System.out.println("Press return to continue...");
		new BufferedReader(new InputStreamReader(System.in)).readLine();
	}

	private static String sep (char c, int times) {
		StringBuffer sbuf = new StringBuffer();
		for (int i = 0; i < times; i++)
			sbuf.append(c);
		return sbuf.toString();
	}

	private static void addMessage () throws Exception {
		MultivaluedMapImpl formData = new MultivaluedMapImpl();

		String username = prompt("Name des Autors");
		formData.add("userId", String.valueOf(getId(username)));
		String msgContent = prompt("Inhalt der Nachricht");
		formData.add("content", msgContent);
		formData.add("parentId", String.valueOf(-1));

		if (post(messagesLink.getHref(), formData) == null) return;

		updateMessages();
		l("Neue Nachricht geadded.");
	}

	private static void addUser () throws Exception {
		MultivaluedMapImpl formData = new MultivaluedMapImpl();
		String username = prompt("Name des neuen Nutzers");
		String password = prompt("Passwort des neuen Nutzers");
		formData.add("name", username);
		formData.add("password", password);

		if (post(usersLink.getHref(), formData) == null) return;

		updateUsers();
		l("Neuer Nutzer geadded. Seine id ist: " + getId(username));
	}

	private static void showMessageList (int userId) throws Exception {
		for (Message m : msgs) {
			if (userId == -1 || m.getUser().getId() == userId) System.out.println(m);
		}

		String choice = prompt("Eine Nachricht rebloggen? (y/n)");
		if (choice.startsWith("y") || choice.startsWith("Y")) {
			int parentId = Integer.valueOf(prompt("ID der zu rebloggenden Nachricht"));

			Message msg = null;

			for (int i = 0; i < msgs.length; i++)
				if (msgs[i].getId() == parentId) msg = msgs[i];

			String reblogLink = msg.getSelf().getHref() + "/reblog";
			String username = prompt("Name des Autors");

			MultivaluedMapImpl formData = new MultivaluedMapImpl();
			formData.add("userId", String.valueOf(getId(username)));

			if (post(reblogLink, formData) == null) return;

			updateMessages();
			l("Neuer Reblog geadded.");
		}
	}

	private static void showAccountList () {
		if (accounts == null) updateAccounts();

		if (!(accounts == null)) {
			for (Account a : accounts) {
				System.out.println(a);
			}
		}
	}

	private static void showUserList () {
		for (User u : users)
			System.out.println(u);
	}

	private static void updateLinks () {
		ClientResponse cresp = acpt("http://localhost:8080/microblog");
		Link[] links = cresp.getEntity(Link[].class);

		usersLink = messagesLink = accountsLink = null;

		// Microblog-Objekt auslesen scheiterte -> Link-Array (Danke Gerrit!)
		String rel;
		for (Link l : links) {
			rel = l.getRel();
			usersLink = rel.equals("users") ? l : usersLink;
			messagesLink = rel.equals("messages") ? l : messagesLink;
			accountsLink = rel.equals("accounts") ? l : accountsLink;
			l("Ressourcenlink: " + l);
		}
	}

	private static void updateAccounts () {
		ClientResponse cresp = acpt(accountsLink.getHref());
		if (cresp == null) {
			l("Nicht autorisiert Accounts einzulesen!");
			return;
		}
		accounts = cresp.getEntity(Account[].class);
		l(accounts.length + " Accounts eingelesen...");
	}

	private static void updateMessages () {
		msgs = acpt(messagesLink.getHref()).getEntity(Message[].class);
		l(msgs.length + " Nachrichten eingelesen...");
	}

	private static void updateUsers () {
		users = acpt(usersLink.getHref()).getEntity(User[].class);
		l(users.length + " Nutzer eingelesen...");
	}

	private static int getId (String name) throws Exception {
		for (User u : users) {
			if (u.getName().equals(name)) return u.getId();
		}

		throw new Exception("Ungueltiger Nutzername!");
	}

	private static String prompt (String msg) throws IOException {
		System.out.print(msg + ": ");
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		return br.readLine();
	}

	private static void l (String s) {
		System.out.println(s);
	}

	private static ClientResponse acpt (String resUri) {
		return unified(resUri, null);
	}

	private static ClientResponse post (String href, MultivaluedMapImpl formData) {
		return unified(href, formData);
	}

	private static ClientResponse unified (String href, MultivaluedMapImpl formData) {
		// first try without basic auth
		ClientResponse cresp = (formData == null) ? c.resource(href).accept("application/xml").get(ClientResponse.class) :
			c.resource(href).type("application/x-www-form-urlencoded").post(ClientResponse.class, formData);

		int status = cresp.getStatus();
		l("Status: " + status + ((status == 401) ? "-> nicht autorisiert!" : ""));

		// haven't tried stored user/pass-pair yet
		boolean triedStored = false;

		while (status == 401) {
			l("Not authorized!");
			try {
				// prompt for user/pass-pair if none stored OR stored already tried
				if (storedUsername == null || storedUsername.length() < 1 || triedStored) {
					storedUsername = prompt("Please enter username (or empty string)");
					if (storedUsername.length() == 0) return null;
					storedPassword = prompt("Please enter password (or empty string)");
					if (storedPassword.length() == 0) return null;
				} else { // haven't prompted -> trying stored
					triedStored = true;
				}

				WebResource res = c.resource(href);
				res.addFilter(new HTTPBasicAuthFilter(storedUsername, storedPassword));

				cresp = (formData == null) ? res.accept("application/xml").get(ClientResponse.class) : res.type("application/x-www-form-urlencoded").post(ClientResponse.class, formData);

				status = cresp.getStatus();
			} catch (Exception e) {
				l("Input error!");
				e.printStackTrace();
			}

		}

		return cresp;
	}

}
